OpenGL Tutorial #2
-- "A Rotating Shaded 3D Object!" --
Introduction
Welcome again! Don't get too excited, it is what you think, using OpenGL to code what you already did, using your own fillers, a rotating shaded 3D object! Enough wasting space, let's begin.
The OpenGL Coordinate System
OpenGL works with its own coordinate system, it's a very simple one, but nevertheless, I should introduce you to it. The centre of the screen is (0, 0). Left and right are given by minus and plus offsets respectively. However, they are not whole numbers, they are between 0 and 1, i.e. the top left of the screen is (-1.0, 1.0) while the bottom left is (-1.0, -1.0) and the middle left is (-1.0, 0). Wtf is the use of that? you might think but actually it means if you change from 640x480 to 800x600, you don't need to change any code, because this coordinate system works independent of screen resolution, it's really a great coordinate system, but it takes a little getting used to.
What about 3d? There is considerable confusion as to whether OpenGL uses a left or right handed coordinate system. It uses a RIGHT handed coordinate system, a z coordinate that is close to us has a positive (big) Z component, and a z coordinate that is far away from us has a negative (small) Z component.
If X/Y coordinate were done in the normal way like when accessing the frame buffer directly, it would be a LEFT handed coodinate system BUT since they don't it's a RIGHT handed coordinate system.
Functions To Work With The OpenGL Coordinate System
The functions will use for now are: glLoadIdentity(), and glTranslatef(GLfloat, GLfloat, GLfloat).
I'll explain what they do, firstly glTranslatef, that function simply moves the viewer a certain amount in the x-axis, in the y-axis and in the z-axis. It moves us however many units we specify away from our CURRENT position, not the origin (0, 0, 0), but from our current position. So if I did this:
glTranslatef(-0.5f, 0.0f, 0.0f); // move us 0.5 to the left
glTranslatef(-0.5f, 0.0f, 0.0f); // and again
glLoadIdentity(); // put us at (0, 0, 0)
glTranslatef(-0.5f, 0.0f, 0.0f); // move us 0.5 to the left
glLoadIdentity(); // and again
glTranslatef(-0.5f, 0.0f, 0.0f); // move us 0.5 to the left
The viewer would move to the left, by 0.5, then flick to the centre again, and then move to the left again. So it'd be pointless, but illustrative. ;)
Finally on this, we can move vertices forward or backward using glTranslatef, if we do so, for example we move forward vertices by a small amount, then -1.0 on the x axis, is no longer the far left side of the screen, but just off the far left side of the screen.
Notes On This Section (i)
(i) As I said in the first tutorial, OpenGL can work with integers too, and
all glXXXXXXf functions have an equivilant glXXXXXXi function. For example
glTranslatef, has the integer equivilant glTranslatei.
(ii) There are other function to work with our coordinate system, e.g. gluLookAt.
Drawing Our First Shapes
Right, now we're getting into it. We're gonna draw some shapes! We'll need to add to the function we wrote it Tutorial #1, DrawScene()
void DrawScene() // This function draws our 3D scene.
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
return;
}
Because we're going to draw something. Lets start with four verticed shapes, in OpenGL these are called "quads". To start drawing a particular shape, we must tell OpenGL like this:
glBegin( a nice shape );
And then we tell OpenGL we've stopped specifiying points by saying:
glEnd();
To actually specify the points we can use a few functions but for now use glVertex3f(x, y, z) it takes three arguments, an X coordinate, a Y coordinate and a Z coordinate. Pretty standard.
Let's add to that function:
void DrawScene() // This function draws our 3D scene.
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glBegin(GL_QUADS); // Lets draw some quads!
glColor3f(0.0f, 0.0f, 1.0f); // Set colour of quad to bright blue.
// It is easier to read code if we indent it:
glVertex3f(-1.0f, 1.0f, 0.0f); // top left
glVertex3f(-1.0f, -1.0f, 0.0f); // bottom left
glVertex3f(1.0f, 1.0f, 0.0f); // top right
glVertex3f(1.0f, -1.0f, 0.0f); // bottom right
glEnd();
return;
}
Wow! That fills the screen blue by drawing a big ol' quad on it. To draw triangles is equally simple.
void DrawScene() // This function draws our 3D scene.
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glBegin(GL_TRIANGLES); // Tell OpenGL we're going to list vertices
glColor3f(0.0f, 0.5f, 1.0f); // a mix of blue and green!
glVertex3f(-1.0f, 1.0f, 0.0f); // top left
glVertex3f(-1.0f, 1.0f, 0.0f); // bottom left
glVertex3f(0.0f, 0.0f, 0.0f); // centre of screen.
glEnd(); // tell OpenGL we've finished listing vertices
return;
}
Faaaantastic. Now, what do you think would happen if you changed the colour while drawing a triangle say, for example, first you set it to red, specify the first vertex, then green and specify the next and then blue and specify the last vertex - I'll tell you what, OpenGL preforms gouraud filler type shading. It'll interpolate the colours to each other, great eh?
Let's draw a smooth shaded triangle:
void DrawScene() // This function draws our 3D scene.
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f); // red at this vertex
glVertex3f(-1.0f, 1.0f, 0.0f); // top left
glColor3f(0.0f, 1.0f, 0.0f); // green at this vertex
glVertex3f(-1.0f, 1.0f, 0.0f); // bottom left
glColor3f(0.0f, 0.0f, 1.0f); // blue at this vertex
glVertex3f(0.0f, 0.0f, 0.0f); // centre of screen.
glEnd();
return;
}
Looks great doesn't it?
Notes On This Section (ii)
(i) This is an extra important section. It teaches us the basics of the
OpenGL coordinate system. Something really essential. It also lays the
basis for shading.
(ii) I'd advise playing around with drawing triangles and quads and shading
them and all that.
What next? (i)
Ok, so what can we do now? we can draw colourful triangles or quads. Now need to rotate them. To rotate a polygon we just use glRotatef It takes four arguments, firstly the angle to rotate by. Then which axes to rotate about, these are the next three arguments, if we give it 1, 0, 0 it rotates about the x-axis by the specified angle, so we see that either a 1 or a 0 tells OpenGL which axes to rotate about. If we called it like this glRotatef( a nice angle, 1, 0, 0);
It would rotate about the x, axis by "a nice angle". A question that should be in your mind is, what are we rotating? We are rotating all the vertices we are about to specify. Let's code a rotating, smooth shaded, triangle.
void DrawScene() // This function draws our 3D scene.
{
static GLfloat Angle = 0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glRotatef(Angle, 0, 0, 1);
Angle += 2;
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(-0.8f, 0.8f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.8f, 0.8f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, -0.8f, 0.0f);
glEnd();
return;
}
Here's something to note, rather than doing this:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glRotatef(Angle, 0, 0, 1);
Angle += 2;
We could do this:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// NO glLoadIdentity()
glRotatef(2, 0, 0, 1);
That just stops the model view matrix getting reset, however, it's better practice to do it the first way, as we all know from coding 3d engines.
Wow. Now we are really cool ;) All we have to do is specify the vertices for a cube, rotate them as we do above...and we have a rotating "shaded" cube, you'll see the coordinates meticulously typed in by yours truely in the source code.
Notes On This Section
(i) This is a basic section, but important. It puts the OpenGL coordinate system into use. Try to specify the coordinates for a cube yourself, rather than just ripping the source code, of course, look at the source to help you, that's what it's there for.
(ii) This is something really important, what I said about glRotatef above is only a half-truth, actually it rotates about an abitrary axis, meaning if I give do : glRotatef(Angle, 0.5f, 0.5f, 0.5f), it'll do a 45 degree tilted rotation of the active modelview matrix, so it rotates about an abitrary axis (or abitrary vector).
Conclusion
You see how easy this stuff is? Ok, the next tutorial is about shading, I don't mean crappy shading, I mean gouraud shading. :] The above type of shading is "constant" shading, meaning it never changes. Gouraud shading changes according to the position of the light source, also next time is texture mapping, and the different filters that go with it and also how to do fog.
send your questions, comments & flames to
paradox of vivid